import { Singleton } from 'decorates';

/**
 * 回调定义
 */
declare interface Callback {
  /**
   * 类型 0 永久 1 单次
   */
  type: number;
  fn: () => void;
}

const INTERVALS = [
  '1s', '5s', '10s', '30s', '1m', '5m'
];

/**
 * 计时器服务，提供定时器
 */
@Singleton()
export default class TimerService {
  private callbacks: {[interval: string]: Callback[]} = {};
  /**
   * 累计秒数
   */
  private ticks = 0;

  constructor () {
    setInterval(this.onTick.bind(this), 1000);
  }

  /**
   * 监听定时器回调
   *
   * @param interval - 事件间隔，必须是 1s、5s、10s、30s 1m、5m 中的1个
   * @param callback - 定时器回调函数
   * @returns 成功返回 `true`
   */
  public on (interval: '1s'|'5s'|'10s'|'30s'|'1m'|'5m', callback: () => void) {
    if (!INTERVALS.includes(interval)) {
      console.error('定时器时间间隔必须为： ' + INTERVALS.join(','));
      return false;
    }
    this.callbacks[interval] = this.callbacks[interval] || [];
    this.callbacks[interval].push({
      type: 0,
      fn: callback
    });
    return true;
  }

  /**
   * 监听定时器回调 单次
   *
   * @param interval - 事件间隔，必须是 1s、5s、10s、30s 1m、5m 中的1个
   * @param callback - 定时器回调函数
   * @returns 成功返回 `true`
   */
  public once (interval: string, callback: () => void) {
    if (!INTERVALS.includes(interval)) {
      console.error('定时器时间间隔必须在 ' + INTERVALS.join(','));
      return false;
    }
    this.callbacks[interval] = this.callbacks[interval] || [];
    this.callbacks[interval].push({
      type: 1,
      fn: callback
    });
    return true;
  }

  /**
   * 取消计时器监听
   *
   * @param interval - 事件间隔，必须是 1s、5s、10s、30s 1m、5m 中的1个
   * @param callback - 定时器回调函数
   * @returns 成功返回 `true`
   */
  public off (interval: string, callback: () => void) {
    if (!this.callbacks[interval]) {
      return false;
    }
    let index = this.callbacks[interval].findIndex(callback);
    if (index > -1) {
      this.callbacks[interval].splice(index, 1);
    }
    return true;
  }

  /**
   * 每秒回调
   */
  private onTick () {
    this.ticks++;
    // 1 秒
    this.fireCallback('1s');
    // 5 秒
    if (this.ticks % 5 === 0) {
      this.fireCallback('5s');
    }
    // 10 秒
    if (this.ticks % 10 === 0) {
      this.fireCallback('10s');
    }
    // 30 秒
    if (this.ticks % 30 === 0) {
      this.fireCallback('30s');
    }
    // 1 分钟
    if (this.ticks % 60 === 0) {
      this.fireCallback('1m');
    }
    // 5 分钟
    if (this.ticks % 300 === 0) {
      this.fireCallback('5m');
    }
  }

  /**
   * 触发定时执行函数
   * @param cb 回调配置
   */
  private fireCallback (cbs: string) {
    let callbacks = this.callbacks[cbs] || [];
    callbacks.forEach(cb => {
      try {
        cb.fn();
      } catch (e) {}
    });
    this.callbacks[cbs] = callbacks.filter(cb => cb.type === 0);
  }
}
